001 /*
002 * Copyright (c) 2005 Stephen J. McConnell
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
013 * implied.
014 *
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018
019 package net.dpml.metro.tools;
020
021 import java.io.File;
022 import java.io.FileOutputStream;
023 import java.io.OutputStream;
024 import java.io.IOException;
025 import java.util.LinkedList;
026 import java.util.List;
027 import java.beans.IntrospectionException;
028
029 import net.dpml.library.info.Scope;
030
031 import net.dpml.metro.info.Type;
032 import net.dpml.metro.builder.ComponentTypeEncoder;
033
034 import net.dpml.tools.tasks.GenericTask;
035
036 import org.apache.tools.ant.AntClassLoader;
037 import org.apache.tools.ant.BuildException;
038 import org.apache.tools.ant.DynamicElementNS;
039 import org.apache.tools.ant.Project;
040 import org.apache.tools.ant.types.Path;
041
042 /**
043 * Task that handles the construction of catalog of type entries.
044 *
045 * @author <a href="http://www.dpml.net">Digital Product Meta Library</a>
046 * @version 1.1.1
047 */
048 public class TypesTask extends GenericTask implements DynamicElementNS
049 {
050 private static final ComponentTypeEncoder COMPONENT_TYPE_ENCODER =
051 new ComponentTypeEncoder();
052
053
054 private List m_builders = new LinkedList();
055
056 /**
057 * Create and return a new type builder.
058 * @return the type builder
059 */
060 public TypeBuilder createType()
061 {
062 Project proj = getProject();
063 TypeBuilderTask builder = new TypeBuilderTask();
064 builder.setProject( proj );
065 m_builders.add( builder );
066 return builder;
067 }
068
069 /**
070 * Operation used to construct a custom part type directive.
071 * @param uri the part handler uri
072 * @param name the element name
073 * @param qualified the qualified name
074 * @return a dynamic type builder
075 */
076 public Object createDynamicElement( String uri, String name, String qualified )
077 {
078 String path = getProject().replaceProperties( uri );
079 TypeBuilder builder = loadTypeBuilder( path, name );
080 if( null != builder )
081 {
082 m_builders.add( builder );
083 }
084 return builder;
085 }
086
087 /**
088 * Task executaion.
089 */
090 public void execute()
091 {
092 Project proj = getProject();
093 Path path = getContext().getPath( Scope.RUNTIME );
094 File classes = getContext().getTargetClassesMainDirectory();
095 path.createPathElement().setLocation( classes );
096 ClassLoader classloader = new AntClassLoader( proj, path );
097 buildTypes( classloader );
098 }
099
100 private void buildTypes( ClassLoader classloader )
101 {
102 final TypeBuilder[] builders = (TypeBuilder[]) m_builders.toArray( new TypeBuilder[0] );
103 ClassLoader current = Thread.currentThread().getContextClassLoader();
104 for( int i=0; i<builders.length; i++ )
105 {
106 final TypeBuilder builder = builders[i];
107 try
108 {
109 final Type type = builder.buildType( classloader );
110 OutputStream output = getOutputStream( type );
111 try
112 {
113 COMPONENT_TYPE_ENCODER.export( type, output );
114 }
115 finally
116 {
117 try
118 {
119 output.close();
120 }
121 catch( IOException ioe )
122 {
123 ioe.printStackTrace();
124 }
125 }
126 }
127 catch( IntrospectionException e )
128 {
129 final String error = e.getMessage();
130 throw new BuildException( error, e, getLocation() );
131 }
132 catch( BuildException e )
133 {
134 throw e;
135 }
136 catch( Throwable e )
137 {
138 final String error =
139 "Internal error while attempting to build types."
140 + "\nCause: " + e.getClass().getName()
141 + "\nMessage: " + e.getMessage();
142 throw new BuildException( error, e, getLocation() );
143 }
144 }
145 }
146
147 private OutputStream getOutputStream( Type type ) throws IOException
148 {
149 final String classname = type.getInfo().getClassname();
150 final String resource = getEmbeddedResourcePath( classname );
151 final File file = getEmbeddedOutputFile( resource );
152 file.getParentFile().mkdirs();
153 return new FileOutputStream( file );
154 }
155
156 private String getEmbeddedResourcePath( String classname )
157 {
158 String path = classname.replace( '.', '/' );
159 String filename = path + ".type";
160 return filename;
161 }
162
163 private File getEmbeddedOutputFile( String filename )
164 {
165 File classes = getContext().getTargetClassesMainDirectory();
166 File destination = new File( classes, filename );
167 return destination;
168 }
169
170 private TypeBuilder loadTypeBuilder( String uri, String name ) throws BuildException
171 {
172 String urn = uri + ":" + name;
173 Object builder = null;
174 TypeBuilder typeBuilder = null;
175 ClassLoader context = Thread.currentThread().getContextClassLoader();
176 try
177 {
178 Thread.currentThread().setContextClassLoader( getClass().getClassLoader() );
179 Project proj = getProject();
180 builder = proj.createDataType( urn );
181 typeBuilder = (TypeBuilder) builder;
182 return typeBuilder;
183 }
184 catch( ClassCastException e )
185 {
186 final String error =
187 "The custom type builder ["
188 + builder.getClass().getName()
189 + "] established by the uri ["
190 + urn
191 + "] declared by the element <"
192 + name
193 + "' does not implement the net.dpml.metro.builder.TypeBuilder interface.";
194 throw new BuildException( error, e, getLocation() );
195 }
196 catch( BuildException e )
197 {
198 final String error =
199 "Unable to load the plugin from the uri ["
200 + urn
201 + "] to handle the custom type declared by the element <"
202 + name
203 + ">.";
204 throw new BuildException( error, e, getLocation() );
205 }
206 catch( Throwable e )
207 {
208 final String error =
209 "Unexpected exception while attempting to load the custom type handler with the uri ["
210 + urn
211 + "] declared by the element <"
212 + name
213 + ">.";
214 throw new BuildException( error, e, getLocation() );
215 }
216 finally
217 {
218 Thread.currentThread().setContextClassLoader( context );
219 }
220 }
221 }